export function patch(oldVnode, vnode) {
    const isRealElement = oldVnode.nodeType;
    if(isRealElement) {
        const oldElm = oldVnode;
        const parentNode = oldElm.parentNode;
        let el = createElm(vnode);
        parentNode.insertBefore(el, oldElm.nextSibling);
        parentNode.removeChild(oldElm);
        return el;
    } else {
        // diff算法
        if(oldVnode.tag !== vnode.tag) {
            // 新旧标签不一致
            oldVnode.el.parentNode.replaceChild(createElm(vnode), oldVnode.el);
        }
        // 如果旧节点是一个文本节点
        if(!oldVnode.tag) {
            if(oldVnode.text !== vnode.next) {
                oldVnode.el.textContent = vnode.text;
            }
        }
        // 以上情况都不符，则代表标签一致，直接复用
        const el = (vnode.el = oldVnode.el);
        updateProperties(vnode, oldVnode.data);
        const oldCh = oldVnode.children || [];
        const newCh = vnode.children || [];
        if(oldCh.length > 0 && newCh.length > 0) {
            updateChildren(el, oldCh, newCh);
        } else if(oldCh.length) {
            el.innerHTML = '';
        } else if(newCh.length) {
            for(let i =0; i < newCh.length; i++) {
                const child = newCh[i];
                el.appendChild(createElm(child));
            }
        }
    }
}

function createElm(vnode) {
    let {tag, data, children, text} = vnode;
    if(typeof tag === 'string') {
        vnode.el = document.createElement(tag);
        updateProperties(vnode);
        children.forEach(child => {
            vnode.el.appendChild(createElm(child));
        })
    } else {
        vnode.el = document.createTextNode(text);
    }
    return vnode.el;
}

function updateProperties(vnode, oldProps = {}) {
    let newProps = vnode.data || {};
    let el = vnode.el;
    // 如果新节点无属性，1、直接删除老节点的属性
    for(const k in oldProps) {
        if(!newProps[k]) {
            el.removeAttribute(k);
        }
    }
    // 如果新节点无属性，2、特殊处理 style，把老的 style 置空
    // const newStyle = newProps.style || {};
    // const oldStyle = oldProps.style || {};
    // for(const k in oldStyle) {
    //     if(!newStyle[k]) {
    //         el.style[k] = '';
    //     }
    // }
    for(let key in newProps) {
        if(key === 'style') {
            for(let styleName in newProps.style) {
                el.style[styleName] = newProps.style[styleName];
            }
        } else if(key === 'class') {
            el.className = newProps.class;
        } else {
            el.setAttribute(key, newProps[key]);
        }
    }
}

function isSameVnode(oldVnode, newVnode) {
    return oldVnode.tag === newVnode.tag && oldVnode.key === newVnode.key;
}

function updateChildren(parent, oldCh, newCh) {
    // 老节点
    let oldSartIndex = 0;
    let oldStartVnode = oldCh[0];
    let oldEndIndex = oldCh.length - 1;
    let oldEndVnode = oldCh[oldEndIndex];
    // 新节点
    let newSartIndex = 0;
    let newStartVnode = newCh[0];
    let newEndIndex = newCh.length - 1;
    let newEndVnode = newCh[newEndIndex];
    // 老儿子的 index 映射表
    function makeIndexByKey(children) {
        let map = {};
        children.forEach((item, index) => {
            map[item.key] = index;
        });
        return map;
    }
    let map = makeIndexByKey(oldCh);
    // 双指针法，不断向中间靠拢来比较虚拟 dom
    while (oldSartIndex <= oldEndIndex && newSartIndex <= newEndIndex) {
        if(!oldStartVnode) {
            // 旧头不存在
            oldStartVnode = oldCh[++oldSartIndex];
        } else if(!oldEndVnode) {
            // 旧尾不存在
            oldEndVnode = oldCh[--oldEndIndex];
        } else if(isSameVnode(oldStartVnode, newStartVnode)) {
            // 旧头和新头对比，依次向后追加
            patch(oldStartVnode, newStartVnode);
            oldStartVnode = oldCh[++oldSartIndex];
            newStartVnode = newCh[++newSartIndex];
        } else if(isSameVnode(oldEndVnode, newEndVnode)) {
            // 旧尾和新尾对比，依次向前追加
            patch(oldEndVnode, newEndVnode);
            oldEndVnode = oldCh[--oldEndIndex];
            newEndVnode = newCh[--newEndIndex];
        } else if(isSameVnode(oldStartVnode, newEndVnode)) {
            // 旧头和新尾相同，则把旧头移动到旧尾部
            patch(oldStartVnode, newEndVnode);
            parent.insertBefore(oldStartVnode.el, oldEndVnode.el.nextSibling);
            oldStartVnode = oldCh[++oldSartIndex];
            newEndVnode = newCh[--newEndIndex];
        } else if(isSameVnode(oldEndVnode, newStartVnode)) {
            // 旧尾和新头相同，则把旧尾移动到旧头部
            patch(oldEndVnode, newStartVnode);
            parent.insertBefore(oldEndVnode.el, oldStartVnode.el);
            oldEndVnode = oldCh[--oldEndIndex];
            newStartVnode = newCh[++newSartIndex];
        } else {
            // 暴力对比
            // 根据老儿子的映射表，从新子节点开始查找，如果能找到就进行移动操作，否则直接插入
            let moveIndex = map[newStartVnode.key];
            if(!moveIndex) {
                parent.insertBefore(createElm(newStartVnode), oldStartVnode.el);
            } else {
                let moveVnode = oldCh[moveIndex];
                oldCh[moveIndex] = undefined; //  这个是占位操作 避免数组塌陷  防止老节点移动走了之后破坏了初始的映射表位置
                parent.insertBefore(moveVnode.el, oldStartVnode.el);
                patch(moveVnode, newStartVnode);
            }
        }
    }
    // 新节点还有，证明这些是需要添加到对应位置
    if(newSartIndex <= newEndIndex) {
        for(let i = newSartIndex; i <= newEndIndex; i++) {
            const ele = newCh[newEndIndex + 1] == null ? null : newCh[newEndIndex + 1].el;
            parent.insertBefore(createElm(newCh[i], ele));
        }
    }
    // 老节点还有, 证明这些是要删除的
    if(oldSartIndex <= oldEndIndex) {
        for(let i = oldSartIndex; i <= oldEndIndex; i++) {
            let child = oldCh[i];
            if(child != undefined) {
                parent.removeChild(child.el);
            }
        }
    }
}